from typing import Any, Optional, Dict, Set

from sqlalchemy import inspect
from sqlalchemy.orm import RelationshipProperty


class ModelConverter:
    class ExtractFieldParam:
        def __init__(
            self,
            origin_list_field: str,
            origin_list_extract_field: str,
            target_list_field: str,
        ):
            self.origin_list_field = origin_list_field
            self.origin_list_extract_field = origin_list_extract_field
            self.target_list_field = target_list_field

    @staticmethod
    def extract_fields_in_model(
        data: Any,
        origin_list_field: str,
        origin_list_extract_field: str,
        target_list_field: str,
    ) -> Any:
        """
        从模型的列表类型字段中提取需要的字段
        :param data: 原模型数据
        :param origin_list_field: 原模型数据要提取的列表类型字段
        :param origin_list_extract_field: 原模型数据要提取的列表子项的字段
        :param target_list_field: 目标模型列表类型字段
        :return: 提取的字段值列表
        """
        if hasattr(data, origin_list_field):
            models = getattr(data, origin_list_field)
            # 如果models存在
            models = models if models is not None else []
            # 确保我们处理的是列表
            models = models if isinstance(models, list) else [models]

            model_fields = [
                getattr(model, origin_list_extract_field)
                for model in models
                if getattr(model, origin_list_extract_field)
            ]
            return {target_list_field: model_fields}
        return None

    @staticmethod
    def extract_in_model_list_fields(
        data: Any,
        origin_model_list_field: str,
        origin_model_list_extract_field: str,
        target_model_field: str,
    ) -> Any:
        """
        从模型的列表类型字段中提取需要的字段
        :param data: 原模型数据
        :param origin_model_list_field: 原模型数据要提取的列表类型字段
        :param origin_model_list_extract_field: 原模型数据要提取的列表子项的字段
        :param target_model_field: 目标模型列表类型字段
        :return: 提取的字段值列表
        """
        model_fields = ModelConverter.extract_fields_in_model(
            data,
            origin_model_list_field,
            origin_model_list_extract_field,
            target_model_field,
        )
        if model_fields:
            return {**data.__dict__, target_model_field: model_fields}
        return data

    @staticmethod
    def extract_multiple_in_model_list_fields(
        data: Any, fields: list[ExtractFieldParam]
    ) -> Any:
        """
        批量从模型的列表类型字段中提取需要的字段
        :param data: 原模型数据
        :param fields: 包含 ['原模型数据列表字段'，'原模型数据列表字段子项的字段'，'目标模型列表类型字段']
        :return:
        """
        result = {}
        for field in fields:
            field_dict = ModelConverter.extract_fields_in_model(
                data,
                field.origin_list_field,
                field.origin_list_extract_field,
                field.target_list_field,
            )
            if field_dict:
                result = {**result, **field_dict}
        if result:
            return {**data.__dict__, **result}
        return data

    @staticmethod
    def tree_model_to_dict(instance: Any) -> dict[Any, Any] | None:
        """
        自引用树形结构 SQLAlchemy 模型转字典方法
        :param instance: SQLAlchemy 模型实例
        :return: 字典格式的数据
        """
        if not instance:
            return None
        # 基础字段
        data = {c.key: getattr(instance, c.key) for c in inspect(instance).mapper.column_attrs}

        # 处理关系字段（如 children）
        for rel in inspect(instance).mapper.relationships:
            if rel.key == "children":  # 显式处理 children
                children = getattr(instance, rel.key)
                data["children"] = [ModelConverter.tree_model_to_dict(child) for child in children] if children else []
        return data

    @staticmethod
    def model_to_dict(
            instance: Any,
            exclude: Optional[Set[str]] = None,
            max_depth: int = 2,
            _visited: Optional[Set[int]] = None,
            _current_depth: int = 0
    ) -> Dict[str, Any]|None:
        """
        通用 SQLAlchemy 模型转字典方法
        :param instance: SQLAlchemy 模型实例
        :param exclude: 要排除的字段名集合
        :param max_depth: 关系字段的最大递归深度
        :param _visited: 已访问的对象ID集合（用于检测循环引用）
        :param _current_depth: 当前递归深度
        :return: 字典格式的数据
        """
        if instance is None:
            return None

        if exclude is None:
            exclude = set()

        if _visited is None:
            _visited = set()

        # 获取对象ID（用于检测循环引用）
        obj_id = id(instance)
        if obj_id in _visited:
            return {"message": "Circular Reference"}
        _visited.add(obj_id)

        # 检查当前深度是否超过限制
        if _current_depth > max_depth:
            return {"message": "Max Depth Reached"}

        # 使用 SQLAlchemy 的 inspect 获取模型信息
        insp = inspect(instance)
        result = {}

        for attr in insp.mapper.attrs:
            # 跳过排除字段
            if attr.key in exclude:
                continue

            # 获取属性值
            value = getattr(instance, attr.key)

            # 处理关系字段（一对多、多对多等）
            if isinstance(attr, RelationshipProperty):
                if value is None:
                    result[attr.key] = None
                elif isinstance(value, (list, set)):
                    result[attr.key] = [
                        ModelConverter.model_to_dict(
                            item,
                            exclude=exclude,
                            max_depth=max_depth,
                            _visited=_visited,
                            _current_depth=_current_depth + 1
                        )
                        for item in value
                    ]
                else:
                    result[attr.key] = ModelConverter.model_to_dict(
                        value,
                        exclude=exclude,
                        max_depth=max_depth,
                        _visited=_visited,
                        _current_depth=_current_depth + 1
                    )
            # 处理普通字段
            else:
                result[attr.key] = value

        return result

